Skip to content

Add agent scope observation and advice tools - #4

Open
Epslion404 wants to merge 2 commits into
Scaxlibur:masterfrom
Epslion404:split/agent-scope-observe-advise
Open

Add agent scope observation and advice tools#4
Epslion404 wants to merge 2 commits into
Scaxlibur:masterfrom
Epslion404:split/agent-scope-observe-advise

Conversation

@Epslion404

Copy link
Copy Markdown
Collaborator

Summary

  • add MCP scope.observe, scope.advise, and doctor.config
  • add waveform expectation checks for frequency, Vpp, mean/offset, duty, and triangle symmetry
  • add pairwise multi-channel relationships including frequency ratio, correlation, delay/phase, and intersections
  • add AI-agent roadmap in TODO.md

Safety

  • MCP tools remain read-only
  • no raw SCPI exposure
  • scope.advise only recommends and never applies changes
  • waveform fetch mode explicitly marks possible waveform-transfer state effects

Validation

  • git diff --cached --check
  • pytest -q tests/test_agent_advise.py tests/test_agent_observe.py tests/test_mcp_http.py tests/test_waveform_expectations.py tests/test_waveform_relationships.py: 45 passed, 1 warning, 3 subtests passed

Split context

This is split out from the original AI-agent scope PR. For the best end-to-end UX, review after the display/focus controls PR, because scope.advise recommends those explicit CLI commands.

@Scaxlibur Scaxlibur left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这组功能方向合理,但当前实现同时存在仪器状态边界和测量正确性问题。尤其是 fetch_waveform=true 会实际改变示波器状态,却仍被暴露为只读工具;多通道关系、expectation 和相位结果也可能给出具有误导性的结论。因此建议先修复以下问题,再合并本 PR。

fetched_waveforms: dict[int, WaveformData],
) -> dict[str, Any]:
service.require_high_impedance(channel, allow_50ohm=allow_50ohm)
waveform = service.fetch_waveform(channel=channel)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] scope.observe(fetch_waveform=true) 最终会调用 ScopeService.fetch_waveform()。对于默认使用 dmax 的 DS1000Z/DS1104Z,该路径会发送 :STOP,同时可能启用通道并修改 waveform source/mode/format,而且当前不会恢复原采集状态。因此一次被描述为“只读”的调用可能让正在运行的示波器停下来。

这里却同时返回 read_only=true/tools 描述也声称不会改变仪器状态,instrument_state_effects 还漏掉了 acquisition stop。建议将 waveform fetch 拆成明确标记为 mutating 的工具并要求显式确认;如果仍保留在 scope.observe,则必须修正工具元数据、文档和完整状态影响说明,并设计失败路径下可证明的状态恢复。

expectations=normalized_expectations,
expectation_results=expectation_results,
)
for observed_channel in observed_channels

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 当前每个通道分别调用 fetch_waveform(),而每次调用还会单独打开 instrument session。示波器处于运行状态时,CH1 和 CH2 可能来自不同 acquisition,但代码仍会计算相位、延迟、相关性和交点,这些结果不能被当作同步测量。

此外,MCP 使用 ThreadingHTTPServer,多个请求还可能同时争用同一台仪器的全局 waveform source。建议在一个持久 session 内冻结或执行一次 acquisition 后批量读取全部通道,并按 instrument resource 序列化访问。若无法证明波形来自同一次 acquisition,应返回 warning,并跳过需要同步性的关系分析。

elif "warn" in statuses:
status = "warn"
else:
status = "pass"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 当 expectation 字段无效、拼写错误或越界时,当前解析逻辑会静默忽略该字段;如果最终没有生成任何 check,这里仍会返回 status="pass"。例如 {"frequency_hz": "typo"}{"vpp_v": -1} 或拼错的 {"frequncy_hz": 1000} 都会得到 pass, checks=[]

MCP 发布的 JSON Schema 并未在服务端真正执行,不能依赖客户端自动遵守。建议在运行时完整校验字段名、类型、有限性和取值范围,遇到无效输入直接返回 ConfigError;如果没有任何可执行检查,状态至少应为 skippederror,不能是 pass

}
pearson = float(np.mean(left * right))
correlation = np.correlate(right, left, mode="full") / left.size
index = int(np.argmax(np.abs(correlation)))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 当前按互相关绝对值选择峰值,随后只根据 lag 计算相位,因此会丢失相关性的正负号。对于 right = -left 的同频正弦,实际结果为 correlation -1、lag 0,最终报告相位 ,但正确结果应为 180°

建议明确相位正负约定,并在相关峰为负时处理额外的 180° 相移,或者改用频域基波相位差。测试也应断言具体角度,而不只是检查结果“不是 None”,至少覆盖 0°、±90° 和 180°。

diffs = np.diff(centered)
if diffs.size < 3:
return None
signs = np.sign(diffs)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 这里直接根据相邻样本差分的符号变化寻找局部极值,对真实示波器噪声和量化误差非常敏感。验证中,30% symmetry、1 Vpp 的三角波加入 1 mV 噪声后被估计为约 5.56%,加入 5 mV 后变成约 50%。目前的理想无噪声测试无法暴露这个问题。

建议先进行受控平滑或滞回处理,并利用期望频率限制相邻周期/极值间距;也可以改为按完整周期做鲁棒斜率或峰谷拟合。请增加包含噪声、量化台阶和轻微过冲的测试样本,避免 expectation 在真实波形上随机 pass/fail。

def _summary_frequency(summary: dict[str, Any] | None) -> float | None:
if summary is None:
return None
value = summary.get("frequency_estimate_hz")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] _summary_frequency() 当前只检查频率是否为正,没有检查 quality_warnings。因此即使 summary 已标记 low_cycle_count,该低置信估计仍会优先于用户提供的 expectation,并被用于生成 scope focus --time-range 建议。

建议复用 relationships 中已有的 trusted-frequency 判断:发现 low_cycle_count 等置信度告警时,优先回退到 expectation;没有可靠 expectation 时则不要生成基于该频率的时基建议,并在结果中明确说明原因。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants